package com.qfedu.mtl.util.web;

import com.qfedu.mtl.util.web.annotation.Controller;
import com.qfedu.mtl.util.web.annotation.RequestMapping;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 应用程序监听器
 * 1.它的主要作用，就是用来初始化（请求和处理器的映射关系）映射表。
 * 2.映射表是一个组 关于  url 和  处理器信息 的 Map集合。ConcurrentHashMap<String, HandlerInfo>
 *
 * 处理思路：
 * 1. 监听网站初始化完成。
 * 2. 查找项目中全部的处理器类
 * 3. 把请求路径  和 处理器信息 存入映射表
 */
@WebListener
public class WebAppListener implements ServletContextListener {
    //扫描的处理器的包名【这里需要改成自己 Controller的包路径】！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！！
    private String basePackage = "com.qfedu.mtl.controller";
    //记录请求和处理器映射关系【映射表】
    private ConcurrentHashMap<String, HandlerInfo> urlHandlerMap = new ConcurrentHashMap<>();
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //获取ServletContext对象，一个网站对应一个ServletContext，这个对象就是全局域对象，可以用它获得网站相关信息。
        ServletContext context = servletContextEvent.getServletContext();
        //获取项目运行时目录，我们知道src是开放目录，项目正在执行时的目录，和运行的代码是out/artifacts/这个目录。
        String webRootPath = context.getRealPath("/");
        //获得控制器包所在目录
        String controllerPath = webRootPath.concat("WEB-INF\\classes\\").concat(basePackage.replace(".", "\\"));
        File controllerDir = new File(controllerPath);
        //获得目录下的文件名字
        String[] list = controllerDir.list();
        //去掉.class后缀
        List<String> classNames = Stream.of(list).map((e) -> e.replace(".class", "")).collect(Collectors.toList());
        //加载这些控制器
        for (String className : classNames) {
            try {
                //使用类加载器价值控制器的字节码文件
                Class cls = context.getClassLoader().loadClass(basePackage.concat(".").concat(className)); //com.qfedu.mtl.controller.UserController
                //判断它是否为控制器类（就是类头上有没有@Controller注解）
                Annotation annotation = cls.getAnnotation(Controller.class);
                if(annotation==null) continue;
                //遍历这些类中的方法，使用 url -> Method 对应。
                Method[] declaredMethods = cls.getDeclaredMethods();
                //遍历方法，判断她们是不是处理请求的方法
                for (Method declaredMethod : declaredMethods) {
                    RequestMapping requestMappingOnMethod = declaredMethod.getAnnotation(RequestMapping.class);
                    //不是处理方法，则跳过
                    if (requestMappingOnMethod==null) continue;
                    //否者取出 该方法处理的url
                    String childUrl = requestMappingOnMethod.value();
                    //查询类级别是否存在 RequestMapping注解
                    String parentUrl= "";
                    Annotation requestMappingOnClass = cls.getDeclaredAnnotation(RequestMapping.class);
                    if (requestMappingOnClass!=null) {
                        RequestMapping mappingOnClass = (RequestMapping) requestMappingOnClass;
                        parentUrl = mappingOnClass.value();
                    }
                    //把映射关系添加倒集合
                    HandlerInfo handlerInfo = new HandlerInfo();
                    handlerInfo.setCls(cls);
                    handlerInfo.setMethod(declaredMethod);
                    handlerInfo.setUrl(parentUrl.concat(childUrl));
                    urlHandlerMap.put(handlerInfo.getUrl(), handlerInfo);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        //最后把 映射表添加倒 全局域中保存
        context.setAttribute("URL_HANDLER_MAP",urlHandlerMap);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}
